package com.shaoyu.tools;

import cn.hutool.core.collection.CollectionUtil;

import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.IntStream;

/**
 * @author shaoyu
 * @date 2023/8/21 10:58
 */
public class PageHelper<T> {
    private List<T> pageData;
    private PageSupplier<T> pageSupplier;
    private int page = 1;
    private int pageSize = 20;
    private Consumer<List<T>> consumer;


    public PageHelper() {
    }

    public static <T> PageHelper<T> builder(Class<T> clz) {
        return new PageHelper<>();
    }

    public static <T> PageHelper<T> builder(Class<T> clz, int pageSize) {
        PageHelper<T> tPageHelper = new PageHelper<>();
        tPageHelper.pageSize = pageSize;
        return tPageHelper;
    }

    public PageHelper<T> pageSupplier(PageSupplier<T> pageSupplier) {
        this.pageSupplier = pageSupplier;
        return this;
    }

    public StartIdPageHelper<T> startIdSupplier(StartIdSupplier<T> startIdSupplier, Function<T, String> startIdResetFunc) {
        StartIdPageHelper<T> startIdPageHelper = new StartIdPageHelper<>(this);
        startIdPageHelper.startIdSupplier = startIdSupplier;
        startIdPageHelper.startIdResetFunc = startIdResetFunc;
        return startIdPageHelper;
    }

    public PageHelper<T> page(int page) {
        this.page = page;
        return this;
    }

    public PageHelper<T> iterator(Consumer<T> singleConsumer) {
        this.consumer = list->list.forEach(singleConsumer);
        return this;
    }

    public PageHelper<T> iteratorList(Consumer<List<T>> consumer) {
        this.consumer = consumer;
        return this;
    }
    public void execute(){
        do {
            this.doFetch();
            this.consumer.accept(this.pageData);
        } while(CollectionUtil.isNotEmpty(this.pageData));
    }

    protected void doFetch() {
        this.pageData = this.pageSupplier.get(this.page, this.pageSize);
        ++this.page;
    }

    public static class StartIdPageHelper<T> extends PageHelper<T>{

        private String startId = "";
        private StartIdSupplier<T> startIdSupplier;
        private Function<T, String> startIdResetFunc;

        public StartIdPageHelper(PageHelper<T> pageHelper){
            super.pageSize = pageHelper.pageSize;
        }
        public StartIdPageHelper<T> startId(String startId) {
            this.startId = startId;
            return this;
        }
        @Override
        protected void doFetch() {
            super.pageData = startIdSupplier.get(startId, super.pageSize);
            if (CollectionUtil.isNotEmpty(super.pageData)) {
                T t = super.pageData.get(super.pageData.size() - 1);
                startId = startIdResetFunc.apply(t);
            }
        }
    }


    public interface StartIdSupplier<T> {
        List<T> get(String startId, int pageSize);
    }

    public interface PageSupplier<T> {
        List<T> get(int page, int pageSize);
    }

    public static void main(String[] args) {
        int[] array = IntStream.range(0, 10000).toArray();

        //前后闭区间
        BiFunction<Integer,Integer,List<Integer>> bif = (s,e)->{
            List<Integer> pageData = new ArrayList<>();
            for(int i = s; i <= e && i < array.length; ++i) {
                pageData.add(array[i]);
            }
            return pageData;
        };

        builder(Integer.class, 10).pageSupplier((page, pageSize) -> {
            int startIndex = (page - 1) * pageSize;
            int endIndex = startIndex + pageSize;
           return bif.apply(startIndex,endIndex);
        }).iterator(System.out::println).execute();

        builder(Integer.class).startIdSupplier((startId, pageSize) -> {
            int startIndex = Integer.parseInt(startId);
            int endIndex = startIndex + pageSize;
            return bif.apply(startIndex,endIndex);
        }, i->String.valueOf(i+1))
                .startId("0")
                .iterator(System.out::println)
                .execute();
    }
}
